/**
 * <pre>
 * Title: 		SequenceServiceImpl.java
 * Project: 	Common-Web
 * Author:		linriqing
 * Create:	 	2009-7-25 下午02:33:55
 * Copyright: 	Copyright (c) 2009
 * Company:		Shenzhen Helper
 * <pre>
 */
package com.hengpeng.common.sequence.impl;

import java.util.HashMap;
import java.util.Map;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import com.hengpeng.common.cis.ComponentContextLoader;
import com.hengpeng.common.persist.AbstractDao;
import com.hengpeng.common.persist.NestedSimplyDao;
import com.hengpeng.common.sequence.SequenceService;
import com.hengpeng.common.sequence.entity.Sequence;

/**
 * <pre>
 * 流水号服务实现
 * </pre>
 * @author linriqing
 * @version 1.0, 2009-7-25
 */
@Component(SequenceService.CONTEXT_BEAN_NAME_SEQUENCESERVICE)
@Lazy
public class SequenceServiceImpl implements SequenceService
{
	class SequenceCacheInfo
	{
		public int cacheCount = 1000;

		public long cacheSequenceNo = 0;

		public boolean isInit = false;

		public long modMaxNum = 100000000;

		public String sequenceName;

		public long usedCount = 0;
	}

	@Repository(SequenceDao_BeanName)
	@Lazy
	public static final class SequenceDaoImpl extends AbstractDao<Sequence>
	{
	}

	private static final String SequenceDao_BeanName = "com.hengpeng.common.sequence.impl.SequenceServiceImpl.SequenceDaoImpl";

	public static final Map<String, SequenceCacheInfo> sequencecache = new HashMap<String, SequenceCacheInfo>();

	/**
	 * <pre>
	 * 线程安全/单例模式的获取实例方法
	 * 注意:对于Spring来说不是安全的单例, 因为Spring会使用反射调用私有的构造函数...
	 * </pre>
	 * @return 流水号服务实现
	 */
	public final static SequenceService getInstance()
	{
		return ComponentContextLoader.getBean(SequenceService.class);
	}

	private NestedSimplyDao<Sequence> sequenceDao;

	@Transactional(propagation = Propagation.REQUIRES_NEW, rollbackFor = Throwable.class)
	public long getSequence(String sequenceName)
	{
		return getSequence(sequenceName, 1);
	}

	@Transactional(propagation = Propagation.REQUIRES_NEW, rollbackFor = Throwable.class)
	public long getSequence(String sequenceName, long sequenceCount)
	{
		return getSequence(sequenceName, 1, null);
	}

	@Override
	@Transactional(propagation = Propagation.REQUIRES_NEW, rollbackFor = Throwable.class)
	public long getSequence(String sequenceName, long sequenceCount, Long modMaxNum)
	{

		long rtnSequence = 0;
		SequenceCacheInfo sequencecacheinfo = this.getSequenceCacheInfo(sequenceName);
		// 一次从数据库中取500个消息流水号保存在静态成员中，先从静态成员分配消息流水号，不够时从数据库再读取500个
		synchronized (sequencecacheinfo)
		{
			// 判断流水号信息是否已经初始化到内存中
			if (!sequencecacheinfo.isInit)
			{
				// 流水号信息还未缓存到内存中, 从数据库查询当前消息流水号到缓存中
				Sequence sequence = sequenceDao.lockById(sequenceName);

				if (sequence == null)
				{
					// 如果数据库中没有消息流水号则新增消息流水号纪录
					sequence = new Sequence();
					sequence.setIndexNum(1L);
					sequence.setName(sequenceName);
					sequence.setCacheCount(sequencecacheinfo.cacheCount);
					if ((modMaxNum != null) && (modMaxNum > 0))
					{
						sequence.setModMaxNum(modMaxNum);
					}
					else
					{
						sequence.setModMaxNum(sequencecacheinfo.modMaxNum);
					}
					sequenceDao.save(sequence);
				}

				// 当前流水号
				sequencecacheinfo.cacheSequenceNo = sequence.getIndexNum();
				sequencecacheinfo.cacheCount = sequence.getCacheCount();
				sequencecacheinfo.modMaxNum = sequence.getModMaxNum();
				sequencecacheinfo.sequenceName = sequence.getName();

				// 取一段流水号
				if (sequenceCount > sequencecacheinfo.cacheCount)
				{
					// 更新后的流水号
					long modAfterSequenceNo = (sequence.getIndexNum() + sequenceCount) % sequencecacheinfo.modMaxNum;
					sequence.setIndexNum(modAfterSequenceNo);
				}
				else
				{
					// 更新后的流水号
					long modAfterSequenceNo = (sequence.getIndexNum() + sequencecacheinfo.cacheCount)
							% sequencecacheinfo.modMaxNum;
					sequence.setIndexNum(modAfterSequenceNo);
				}
				// 保存本次批量投注流水号
				sequenceDao.update(sequence);

				// 当前获取的流水号
				rtnSequence = sequencecacheinfo.cacheSequenceNo;
				// 更新获取后缓存中的流水号
				sequencecacheinfo.cacheSequenceNo = sequencecacheinfo.cacheSequenceNo + sequenceCount;
				// 更新获取后缓存中已用掉的流水号个数
				sequencecacheinfo.usedCount = sequenceCount;
				// 设置缓存初始化状态
				sequencecacheinfo.isInit = true;
			}
			else
			{
				// 已经初始化到内存中
				// 判断缓存的流水号是否够用
				if ((sequencecacheinfo.usedCount + sequenceCount) > sequencecacheinfo.cacheCount)
				{
					// 不够用, 只能浪费剩下的流水号了, 需要重新取出一批流水号
					// 从数据库同步当前消息流水号到缓存中
					Sequence sequence = sequenceDao.lockById(sequenceName);

					// 当前流水号
					sequencecacheinfo.cacheSequenceNo = sequence.getIndexNum();
					sequencecacheinfo.cacheCount = sequence.getCacheCount();
					sequencecacheinfo.modMaxNum = sequence.getModMaxNum();
					sequencecacheinfo.sequenceName = sequence.getName();

					// 取一段流水号
					if (sequenceCount > sequencecacheinfo.cacheCount)
					{
						// 更新后的流水号
						long modAfterSequenceNo = (sequence.getIndexNum() + sequenceCount)
								% sequencecacheinfo.modMaxNum;
						sequence.setIndexNum(modAfterSequenceNo);
					}
					else
					{
						// 更新后的流水号
						long modAfterSequenceNo = (sequence.getIndexNum() + sequencecacheinfo.cacheCount)
								% sequencecacheinfo.modMaxNum;
						sequence.setIndexNum(modAfterSequenceNo);
					}
					// 保存本次批量投注流水号
					sequenceDao.update(sequence);

					// 当前获取的流水号
					rtnSequence = sequencecacheinfo.cacheSequenceNo;
					// 更新获取后缓存中的流水号
					sequencecacheinfo.cacheSequenceNo = sequencecacheinfo.cacheSequenceNo + sequenceCount;
					// 更新获取后缓存中已用掉的流水号个数
					sequencecacheinfo.usedCount = sequenceCount;
				}
				else
				{
					// 仍然够用, 直接在缓存中同步
					// 当前获取的流水号
					rtnSequence = sequencecacheinfo.cacheSequenceNo;
					// 更新获取后缓存中的流水号
					sequencecacheinfo.cacheSequenceNo = sequencecacheinfo.cacheSequenceNo + sequenceCount;
					// 更新获取后缓存中已用掉的流水号个数
					sequencecacheinfo.usedCount = sequencecacheinfo.usedCount + sequenceCount;
				}
			}

		}
		return rtnSequence;
	}

	private SequenceCacheInfo getSequenceCacheInfo(String sequenceName)
	{
		if (sequencecache.containsKey(sequenceName))
		{
			return sequencecache.get(sequenceName);
		}
		else
		{
			synchronized (sequencecache)
			{
				if (sequencecache.containsKey(sequenceName))
				{
					return sequencecache.get(sequenceName);
				}
				else
				{
					SequenceCacheInfo sequencecacheinfo = new SequenceCacheInfo();
					sequencecache.put(sequenceName, sequencecacheinfo);
					return sequencecacheinfo;
				}
			}
		}
	}

	@Autowired(required = false)
	@Qualifier(SequenceDao_BeanName)
	public void setSequenceDao(NestedSimplyDao<Sequence> sequenceDao)
	{
		this.sequenceDao = sequenceDao;
	}
}
